
class Standard_VHD_File

  LABEL_LENGTH = 23 
  CLOCK_CYCLES = 16

  def initialize( file_in_obj, easy_link_obj )
    @file = file_in_obj
    @easy_link = easy_link_obj
  end

  def write_beginning

    # create headers
    @file.print("library ieee;\n")
    @file.print("use ieee.std_logic_1164.all;\n")
    @file.print("use ieee.numeric_std.all;\n")
    @file.print("\n")

    # declare testbench entity
    @file.print("entity generated_controller_tb is\n")
    @file.print("end entity generated_controller_tb;\n")
    @file.print("\n")

    # open architecture statement
    @file.print("architecture tb of generated_controller_tb is\n\n")

  end

  def write_components

    # generate comment header for components
    @file.print("\t------------------------------------------------------------------------------------------\n");
    @file.print("\t--\n")
    @file.print("\t-- components\n")
    @file.print("\t--\n")
    @file.print("\t-- components used are defined here\n")
    @file.print("\t--\n")
    @file.print("\t------------------------------------------------------------------------------------------\n\n");

    # generate component listings
    @file.print("\tcomponent link_controller is\n\n")
    @file.print("\t\tport ( clk               : in  std_logic;\n")
    @file.print("\t\t       host_address      : in  std_logic_vector(#{LABEL_LENGTH} DownTo 0);\n")
    @file.print("\t\t       pixel_value       : in  std_logic;\n")
    @file.print("\t\t       data_in_n         : in  std_logic;\n")
    @file.print("\t\t       data_in_ne        : in  std_logic;\n")
    @file.print("\t\t       data_in_nw        : in  std_logic;\n")
    @file.print("\t\t       data_in_s         : in  std_logic;\n")
    @file.print("\t\t       data_in_se        : in  std_logic;\n")
    @file.print("\t\t       data_in_sw        : in  std_logic;\n")
    @file.print("\t\t       data_in_e         : in  std_logic;\n")
    @file.print("\t\t       data_in_w         : in  std_logic;\n")
    @file.print("\t\t       data_out_n        : out std_logic;\n")
    @file.print("\t\t       data_out_ne       : out std_logic;\n")
    @file.print("\t\t       data_out_nw       : out std_logic;\n")
    @file.print("\t\t       data_out_s        : out std_logic;\n")
    @file.print("\t\t       data_out_se       : out std_logic;\n")
    @file.print("\t\t       data_out_sw       : out std_logic;\n")
    @file.print("\t\t       data_out_e        : out std_logic;\n")
    @file.print("\t\t       data_out_w        : out std_logic;\n")
    @file.print("\t\t       links_complete    : out std_logic;\n")
    @file.print("\t\t       controller_label  : out std_logic_vector(#{LABEL_LENGTH} DownTo 0)\n")
    @file.print("\t\t     );\n\n")
    @file.print("\tend component;\n\n")

    @file.print("\tcomponent link_physical is \n\n")
    @file.print("\t\tport ( link_a    : in  std_logic;\n")
    @file.print("\t\t       link_b    : in  std_logic;\n")
    @file.print("\t\t       or_out    : out std_logic\n")
    @file.print("\t\t     );\n\n")
    @file.print("\tend component;\n\n")

  end

  def write_global_signals

    # generate comment header for global signals
    @file.print("\t------------------------------------------------------------------------------------------\n");
    @file.print("\t--\n")
    @file.print("\t-- global signals\n")
    @file.print("\t--\n")
    @file.print("\t-- all global signals used across controllers are defined here\n")
    @file.print("\t-- \n")
    @file.print("\t------------------------------------------------------------------------------------------\n\n");

    @file.print( line_up_scribble( "\tsignal controller_clk", ": std_logic;\n\n", 50 ) )


  end

  def write_controller_signals

    @file.print("\t------------------------------------------------------------------------------------------\n");
    @file.print("\t--\n")
    @file.print("\t-- controller link signals\n")
    @file.print("\t--\n")
    @file.print("\t-- the physical links between each controller\n")
    @file.print("\t-- \n")
    @file.print("\t------------------------------------------------------------------------------------------\n\n");

    symbol_array = [:N, :NE, :NW, :S, :SE, :SW, :E, :W]
    symbol_str = ["n", "ne", "nw", "s", "se", "sw", "e", "w"]

    buff = Array.new
    
    for controller_array in @easy_link.controller_matrix

      for controller in controller_array

        buff = push_if_does_not_exist( buff, "\t-- controller #{controller}\n" )
        buff = push_if_does_not_exist( buff, get_address_str(controller) )
        buff = push_if_does_not_exist( buff, get_pixel_str(controller) )
        buff = push_if_does_not_exist( buff, get_complete_str(controller) )
        buff = push_if_does_not_exist( buff, get_f_label_str(controller) )

        # do north
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[0] ) ) )
          buff = push_if_does_not_exist( buff, get_phys_link_str( neighbor, controller ) )
        else
          buff = push_if_does_not_exist( buff, get_phys_loop_str( controller, symbol_str[0] ) )
        end

        # do north east
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[1] ) ) )
          buff = push_if_does_not_exist( buff, get_phys_link_str( neighbor, controller ) )
        else
          buff = push_if_does_not_exist( buff, get_phys_loop_str( controller, symbol_str[1] ) )
        end

        # do north west
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[2] ) ) )
          buff = push_if_does_not_exist( buff, get_phys_link_str( neighbor, controller ) )
        else
          buff = push_if_does_not_exist( buff, get_phys_loop_str( controller, symbol_str[2] ) )
        end

        # do south
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[3] ) ) )
          buff = push_if_does_not_exist( buff, get_phys_link_str( controller, neighbor ) )
        else
          buff = push_if_does_not_exist( buff, get_phys_loop_str( controller, symbol_str[3] ) )
        end

        # do south east
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[4] ) ) )
          buff = push_if_does_not_exist( buff, get_phys_link_str( controller, neighbor ) )
        else
          buff = push_if_does_not_exist( buff, get_phys_loop_str( controller, symbol_str[4] ) )
        end

        # do south west
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[5] ) ) )
          buff = push_if_does_not_exist( buff, get_phys_link_str( controller, neighbor ) )
        else
          buff = push_if_does_not_exist( buff, get_phys_loop_str( controller, symbol_str[5] ) )
        end

        # do east
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[6] ) ) )
          buff = push_if_does_not_exist( buff, get_phys_link_str( controller, neighbor ) )
        else
          buff = push_if_does_not_exist( buff, get_phys_loop_str( controller, symbol_str[6] ) )
        end

        # do west
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[7] ) ) )
          buff = push_if_does_not_exist( buff, get_phys_link_str( neighbor, controller ) )
        else
          buff = push_if_does_not_exist( buff, get_phys_loop_str( controller, symbol_str[7] ) )
        end

        symbol_str = ["n", "ne", "nw", "s", "se", "sw", "e", "w"]
        for sym in symbol_str
          buff = push_if_does_not_exist( buff, get_out_str( controller, sym) ) 
        end

        buff.push( "\n" )

      end

    end

    for str in buff
      @file.print( str )
    end

  end

  def push_if_does_not_exist( array, str )
    if ( nil == array.index( str ) )
      array.push( str )
    end
    return array
  end

  def write_controllers

    @file.print( "\nbegin\n\n" )

    @file.print("\t------------------------------------------------------------------------------------------\n");
    @file.print("\t--\n")
    @file.print("\t-- controllers\n")
    @file.print("\t--\n")
    @file.print("\t-- all controllers and signals are linked here\n")
    @file.print("\t-- \n")
    @file.print("\t------------------------------------------------------------------------------------------\n\n");

    symbol_array = [:N, :NE, :NW, :S, :SE, :SW, :E, :W]
    symbol_str = ["n", "ne", "nw", "s", "se", "sw", "e", "w"]
    
    for controller_array in @easy_link.controller_matrix

      for controller in controller_array

        @file.print( "\t-- controller #{controller} decleration\n" )

        tempString = line_up_scribble( "\tcontroller_#{controller}", ": link_controller port map (", 30 )
        tempString = line_up_scribble( tempString, "controller_clk,", 60 )

        @file.print( tempString + "\n" )
        @file.print( line_up_scribble( "\t", "controller_#{controller}_address,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "controller_#{controller}_pixel,\n", 60) )

        # do north
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[0] ) ) )
          @file.print( line_up_scribble( "\t", "ph_link_#{neighbor}_#{controller},\n", 60 ) )
        else
          @file.print( line_up_scribble( "\t", "ph_loop_#{controller}_n,\n", 60 ) )
        end

        # do north east
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[1] ) ) )
          @file.print( line_up_scribble( "\t", "ph_link_#{neighbor}_#{controller},\n", 60 ) )
        else
          @file.print( line_up_scribble( "\t", "ph_loop_#{controller}_ne,\n", 60 ) )
        end

        # do north west
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[2] ) ) )
          @file.print( line_up_scribble( "\t", "ph_link_#{neighbor}_#{controller},\n", 60 ) )
        else
          @file.print( line_up_scribble( "\t", "ph_loop_#{controller}_nw,\n", 60 ) )
        end

        # do south
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[3] ) ) )
          @file.print( line_up_scribble( "\t", "ph_link_#{controller}_#{neighbor},\n", 60 ) )
        else
          @file.print( line_up_scribble( "\t", "ph_loop_#{controller}_s,\n", 60 ) )
        end

        # do south east
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[4] ) ) )
          @file.print( line_up_scribble( "\t", "ph_link_#{controller}_#{neighbor},\n", 60 ) )
        else
          @file.print( line_up_scribble( "\t", "ph_loop_#{controller}_se,\n", 60 ) )
        end

        # do south west
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[5] ) ) )
          @file.print( line_up_scribble( "\t", "ph_link_#{controller}_#{neighbor},\n", 60 ) )
        else
          @file.print( line_up_scribble( "\t", "ph_loop_#{controller}_sw,\n", 60 ) )
        end

        # do east
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[6] ) ) )
          @file.print( line_up_scribble( "\t", "ph_link_#{controller}_#{neighbor},\n", 60 ) )
        else
          @file.print( line_up_scribble( "\t", "ph_loop_#{controller}_e,\n", 60 ) )
        end

        # do west
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, symbol_array[7] ) ) )
          @file.print( line_up_scribble( "\t", "ph_link_#{neighbor}_#{controller},\n", 60 ) )
        else
          @file.print( line_up_scribble( "\t", "ph_loop_#{controller}_w,\n", 60 ) )
        end

        @file.print( line_up_scribble( "\t", "link_#{controller}_out_n,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "link_#{controller}_out_ne,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "link_#{controller}_out_nw,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "link_#{controller}_out_s,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "link_#{controller}_out_se,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "link_#{controller}_out_sw,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "link_#{controller}_out_e,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "link_#{controller}_out_w,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "controller_#{controller}_complete,\n", 60 ) )
        @file.print( line_up_scribble( "\t", "controller_#{controller}_f_label\n", 60 ) )
        @file.print( line_up_scribble( "\t", ");", 57) )

        @file.print( "\n" )

      end

    end


  end

  def get_link_str( start, start_dir, fin, fin_dir )

    str_buff = line_up_scribble( "\tlink_#{start}_#{fin}", ": link_physical port map (", 23 )
    str_buff = line_up_scribble( str_buff, "link_#{start}_out_#{start_dir}", 52 )
    str_buff = line_up_scribble( str_buff, ", link_#{fin}_out_#{fin_dir}", 76 )
    str_buff = line_up_scribble( str_buff, ", ph_link_#{start}_#{fin}", 100 )
    str_buff = line_up_scribble( str_buff, ");\n", 123 )

  end

  def get_loop_link_str( start, start_dir )

    str_buff = line_up_scribble( "\tlink_l_#{start}_#{start_dir}", ": link_physical port map (", 23 )
    str_buff = line_up_scribble( str_buff, "link_#{start}_out_#{start_dir}", 52 )
    str_buff = line_up_scribble( str_buff,  ", link_#{start}_out_#{start_dir}", 76 )
    str_buff = line_up_scribble( str_buff, ", ph_loop_#{start}_#{start_dir}", 100 )
    str_buff = line_up_scribble( str_buff, ");\n", 123 )

  end

  def write_physical_links

    @file.print("\t------------------------------------------------------------------------------------------\n");
    @file.print("\t--\n")
    @file.print("\t-- physical links\n")
    @file.print("\t--\n")
    @file.print("\t-- physical links I/O defined here\n")
    @file.print("\t-- \n")
    @file.print("\t------------------------------------------------------------------------------------------\n\n");
    buff = Array.new
    
    for controller_array in @easy_link.controller_matrix

      for controller in controller_array

        # do north
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, :N ) ) )
          push_if_does_not_exist( buff, get_link_str( neighbor, "s", controller, "n" ) )
        else
          push_if_does_not_exist( buff, get_loop_link_str( controller, "n" ) )
        end

        # do north east
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, :NE ) ) )
          push_if_does_not_exist( buff, get_link_str( neighbor, "sw", controller, "ne" ) )
        else
          push_if_does_not_exist( buff, get_loop_link_str( controller, "ne" ) )
        end

        # do north west
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, :NW ) ) )
          push_if_does_not_exist( buff, get_link_str( neighbor, "se", controller, "nw" ) )
        else
          push_if_does_not_exist( buff, get_loop_link_str( controller, "nw" ) )
        end

        # do south
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, :S ) ) )
          push_if_does_not_exist( buff, get_link_str( controller, "s", neighbor, "n" ) )
        else
          push_if_does_not_exist( buff, get_loop_link_str( controller, "s" ) )
        end

        # do south east
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, :SE ) ) )
          push_if_does_not_exist( buff, get_link_str( controller, "se", neighbor, "nw" ) )
        else
          push_if_does_not_exist( buff, get_loop_link_str( controller, "se" ) )
        end

        # do south west
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, :SW ) ) )
          push_if_does_not_exist( buff, get_link_str( controller, "sw", neighbor, "ne" ) )
        else
          push_if_does_not_exist( buff, get_loop_link_str( controller, "sw" ) )
        end

        # do east
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, :E ) ) )
          push_if_does_not_exist( buff, get_link_str( controller, "e", neighbor, "w" ) )
        else
          push_if_does_not_exist( buff, get_loop_link_str( controller, "e" ) )
        end

        # do west
        if ( nil != ( neighbor = @easy_link.getControllerLink( controller, :W ) ) )
          push_if_does_not_exist( buff, get_link_str( neighbor, "e", controller, "w" ) )
        else
          push_if_does_not_exist( buff, get_loop_link_str( controller, "w" ) )
        end

        buff.push( "\n" )

      end

    end

    for str in buff
      @file.print( str )
    end

    @file.print( "\n" )

  end

  def write_process 

    @file.print( "\tprocess\n\n" )

    @file.print( "\t\tvariable out_loop : integer := 0;\n\n\n" )

    @file.print( "\t\tprocedure clock_link is\n" )
    @file.print( "\t\tbegin\n\n" )
    @file.print( "\t\t\tcontroller_clk <= '1';\n" )
    @file.print( "\t\t\twait for 1 ns;\n" )
    @file.print( "\t\t\tcontroller_clk <= '0';\n" )
    @file.print( "\t\t\twait for 1 ns;\n\n" )
    @file.print( "\t\tend clock_link;\n\n" )

    @file.print( "\t\tprocedure print_any_label ( print_label : std_logic_vector ) is\n" )
    @file.print( "\t\tbegin\n\n" )
    @file.print(line_up_scribble("\t\t\treport (\"Printing Lablel:\"","& std_logic'image(print_label(#{LABEL_LENGTH}))\n", 30))
    for num in (1..(LABEL_LENGTH-1))
      @file.print(line_up_scribble("\t\t\t",  "& std_logic'image(print_label(#{LABEL_LENGTH - num}))\n", 30))
    end
    @file.print(line_up_scribble("\t\t\t",  "& std_logic'image(print_label(0)));\n\n", 30))
    @file.print( "\t\tend procedure print_any_label;\n\n" )

    @file.print( "\t\tprocedure print_all_final_labels is\n" )
    @file.print( "\t\tbegin\n\n" )
    for num in (1..(@easy_link.get_max_controller))
      @file.print( "\t\t\tprint_any_label(controller_#{num}_f_label);\n" )
    end
    @file.print( "\n\t\tend procedure print_all_final_labels;\n\n" )

    @file.print( "\t\timpure function are_all_controllers_complete return std_logic is\n" )
    @file.print( "\t\t\tvariable result : std_logic := '1';\n" )
    @file.print( "\t\tbegin\n\n" )
    for num in (1..(@easy_link.get_max_controller))
      @file.print( "\t\t\tresult := result and controller_#{num}_complete;\n" )
    end
    @file.print( "\t\t\treturn result;\n\n" )
    @file.print( "\t\tend function are_all_controllers_complete;\n\n" )

    @file.print( "\t\tbegin\n\n" )

    @file.print( "\t\t\t-- set  controller pixel values\n" )

    # TODO: this is where we would put the correct pixels for a binary pic
    for num in (1..(@easy_link.get_max_controller))
      @file.print( line_up_scribble("\t\t\tcontroller_#{num}_pixel", "<= '1';\n", 30 ) )
    end

    @file.print( "\n" )

    for num in (0..@easy_link.get_max_boundary_controller)
      @file.print( line_up_scribble("\t\t\tcontroller_b#{num}_pixel", "<= '0';\n", 30 ) )
    end
    @file.print( "\n\t\t\twait for 1 ns;\n\n")

    @file.print( "\t\t\t--set controller addresses\n" )
    for num in (1..(@easy_link.get_max_controller))
      @file.print( line_up_scribble("\t\t\tcontroller_#{num}_address", 
                                    "<= \"#{get_32_bit_binary_str( num )}\";\n", 30 ) )
    end
    @file.print( "\n" )
    for num in (0..(@easy_link.get_max_boundary_controller))
      @file.print( line_up_scribble("\t\t\tcontroller_b#{num}_address", 
                                    "<= \"#{get_32_bit_binary_str( 8388608 )}\";\n", 30 ) )
    end

    @file.print( "\n\t\t\twait for 1 ns;\n\n")

    @file.print( "\t\t\t-- initialize contollers and their links with 3 clock cycles\n" )
    @file.print( "\t\t\tclock_link;\n\t\t\tclock_link;\n\t\t\tclock_link;\n\n" )

    @file.print( "\t\t\twhile are_all_controllers_complete = '0' loop\n\n" )
    @file.print( "\t\t\t\tout_loop := out_loop + 1;\n" )

    CLOCK_CYCLES.times { @file.print( "\t\t\t\tclock_link;\n" ) }

    @file.print( "\n" )

    @file.print( "\t\t\tend loop;\n\n" )


    @file.print( "\t\t\t-- print final labels\n" )
    @file.print( "\t\t\treport (\"Number of Communication Cycles: \" & integer'image( out_loop ) );\n" )
    @file.print( "\t\t\tprint_all_final_labels;\n\n" )

    @file.print( "\t\t\twait;\n\n" )
    @file.print( "\t\tend process;\n\n" )

  end

  def write_closing

    # closing the architecture
    @file.print("end tb;\n\n")
   
  end

  def get_phys_link_str( first, last )
    return line_up_scribble( "\tsignal ph_link_#{first}_#{last}", ": std_logic;\n", 50 )
  end

  def get_phys_loop_str( controller, dir_str )
    return line_up_scribble( "\tsignal ph_loop_#{controller}_#{dir_str}", ": std_logic;\n", 50 )
  end

  def get_address_str( controller )
    return line_up_scribble( "\tsignal controller_#{controller}_address", ": std_logic_vector(#{LABEL_LENGTH} DownTo 0);\n", 50 )
  end

  def get_pixel_str( controller )
    return line_up_scribble( "\tsignal controller_#{controller}_pixel", ": std_logic;\n", 50 )
  end

  def get_complete_str( controller )
    return line_up_scribble( "\tsignal controller_#{controller}_complete", ": std_logic;\n", 50 )
  end

  def get_f_label_str( controller )
    return line_up_scribble( "\tsignal controller_#{controller}_f_label", ": std_logic_vector(#{LABEL_LENGTH} DownTo 0);\n", 50 )
  end

  def get_out_str( controller, dir_str )
    return line_up_scribble( "\tsignal link_#{controller}_out_#{dir_str}", ": std_logic;\n", 50 )
  end

  def get_32_bit_binary_str( target_num )
    
    result = ""
    num = target_num

    (LABEL_LENGTH + 1).times { |i|

      temp = num.divmod( 2**(LABEL_LENGTH - i) )

      if ( temp[0] == 1 )
        result = result + "1"
      else
        result = result + "0"
      end

      num = temp[1]

    }

    return result

  end

  def line_up_scribble( start_string, end_string, offset )

    string_buff = start_string

    (offset - start_string.length).times {
      string_buff = string_buff + " "
    }

    string_buff = string_buff + end_string

  end

end
















